﻿namespace Busconeitor.BusinessLayer.Util
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Text.RegularExpressions;

    public class BusconeitorCore
    {
        public BusconeitorCore()
        {
            var docs = new List<string>
                {
                    "gold silver truck", // Query
                    "shipment of gold damaged in a fire", // Doc1
                    "delivery of silver arrived in a silver truck", // Doc2
                    "shipment of gold arrived in a truck", // Doc3
                    "a truck of gold with silver surfer" 
                };

            this.SearchWithVectorSpaceModel(docs);
        }

        private void SearchWithVectorSpaceModel(List<string> docs)
        {
            var documentTermVector = new Hashtable(); // Hashtable to hold Document Term Vector
            var wordlist = new List<string>(); // List of terms found in documents
            var sortedList = new Dictionary<double, string>(); // Documents ranked by VSM with angle value

            CrearListaPalabras(wordlist, docs);
            CrearVector(documentTermVector, wordlist, docs);
            Clasificar(documentTermVector, wordlist, sortedList);

            var dictionary = sortedList;

            foreach (var item in dictionary.Reverse())
            {
                Console.WriteLine("{0} -> Doc{1}", item.Key, item.Value);
            }
        }

        /// <summary>
        /// Crea una lista de palabras a partir de un conjunto de documentos
        /// </summary>
        /// <param name="listaPalabras"></param>
        /// <param name="documentos"></param>
        private void CrearListaPalabras(List<string> listaPalabras, List<string> documentos)
        {
            foreach (var doc in documentos)
            {
                listaPalabras = this.ObtenerListaPalabras(listaPalabras, doc);
            }
        }

        private List<string> ObtenerListaPalabras(List<string> listaPalabras, string consulta)
        {
            var expresion = new Regex("\\w+", RegexOptions.IgnoreCase);
            var matchCollection = expresion.Matches(consulta);

            foreach (Match match in matchCollection)
            {
                if (!listaPalabras.Contains(match.Value))
                {
                    listaPalabras.Add(match.Value);
                }
            }

            return listaPalabras;
        }

        private void CrearVector(Hashtable documentTermVector, List<string> listaPalabras, List<string> documentos)
        {
            for (var j = 0; j < documentos.Count; j++)
            {
                var queryVector = new double[listaPalabras.Count];

                for (var i = 0; i < listaPalabras.Count; i++)
                {
                    var termFrecuencyIdFrecuency = this.ObtenerFrecuenciaTermino(documentos[j], listaPalabras[i]) * this.ObtenerIdFrecuencia(listaPalabras[i], documentos);
                    queryVector[i] = termFrecuencyIdFrecuency;
                }

                if (j == 0)
                {
                    // is it a query?
                    documentTermVector.Add("Query", queryVector);
                }
                else
                {
                    documentTermVector.Add(j.ToString(CultureInfo.InvariantCulture), queryVector);
                }
            }
        }

        private void Clasificar(Hashtable documentTermVector, List<string> wordlist, Dictionary<double, string> sortedList)
        {
            var enumerator = documentTermVector.GetEnumerator();
            var queryVector = new double[wordlist.Count];

            Array.Copy((double[])documentTermVector["Query"], queryVector, wordlist.Count);

            while (enumerator.MoveNext())
            {
                if (enumerator.Key.ToString() != "Query")
                {
                    var temp = this.CosineTheta(queryVector, (double[])enumerator.Value);
                    sortedList.Add(temp, enumerator.Key.ToString());
                }
            }

            // foreach (string item in documentTermVector.Keys)
            // {
            //     if (item != "Query")
            //     {
            //         var temp = CosineTheta(queryVector, (double[])enumerator.Value);
            //         sortedList.Add(temp, item);
            //     }
            // }
        }

        private double DotProduct(double[] v1, double[] v2)
        {
            var product = 0.0;

            if (v1.Length == v2.Length)
            {
                for (var i = 0; i < v1.Length; i++)
                {
                    product += v1[i] * v2[i];
                }
            }

            return product;
        }

        private double VectorLength(double[] vector)
        {
            var length = 0.0;

            for (var i = 0; i < vector.Length; i++)
            {
                length += Math.Pow(vector[i], 2);
            }

            return Math.Sqrt(length);
        }

        private double ObtenerFrecuenciaTermino(string documento, string termino)
        {
            var queryTerms = Regex.Split(documento, "\\s");
            var count = 0.0;

            foreach (var t in queryTerms)
            {
                if (t == termino)
                {
                    count++;
                }
            }

            return count;
        }

        private double ObtenerIdFrecuencia(string term, List<string> documentos)
        {
            var frecuenciaDocumento = 0.0;

            // get term frequency of all of the sentences except for the query
            for (var i = 1; i < documentos.Count; i++)
            {
                if (documentos[i].Contains(term))
                {
                    frecuenciaDocumento++;
                }
            }

            // get sentence count
            double documento = documentos.Count - 1; // excluding the query
            var idFrecuencia = 0.0;

            if (frecuenciaDocumento > 0)
            {
                idFrecuencia = Math.Log(documento / frecuenciaDocumento);
            }

            return idFrecuencia;
        }

        private double CosineTheta(double[] vector1, double[] vector2)
        {
            var lengthVector1 = this.VectorLength(vector1);
            var lengthVector2 = this.VectorLength(vector2);
            var dotProduct = this.DotProduct(vector1, vector2);

            return dotProduct / (lengthVector1 * lengthVector2);
        }
    }
}